home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / emacs.lha / emacs-19.16 / lisp / cmacexp.el < prev    next >
Lisp/Scheme  |  1993-04-26  |  17KB  |  455 lines

  1. ;;; cmacexp.el --- expand C macros in a region
  2.  
  3. ;; Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Francesco Potorti` <pot@cnuce.cnr.it>
  6. ;; Version: $Id: cmacexp.el 1.2 1992/09/15 11:34:56 pot Exp $
  7. ;; Adapted-By: ESR
  8. ;; Keywords: c
  9.  
  10. ;; This file is part of GNU Emacs.
  11.  
  12. ;; GNU Emacs is free software; you can redistribute it and/or modify
  13. ;; it under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your option)
  15. ;; any later version.
  16.  
  17. ;; GNU Emacs is distributed in the hope that it will be useful,
  18. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. ;; GNU General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  24. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  25.  
  26. ;;; Commentary:
  27.  
  28. ;; In C mode C-M-x is bound to c-macro-expand.  The result of the
  29. ;; expansion is put in a separate buffer.  The buffer is put in
  30. ;; view-mode if the Inge Frick's view.el is installed.  A user option
  31. ;; allows the window displaying the buffer to be optimally sized.
  32. ;;
  33. ;; When called with a C-u prefix, c-macro-expand replaces the selected
  34. ;; region with the expansion.  With two C-u's the user is offered to
  35. ;; change the flags to the preprocessor (while the results of the
  36. ;; expansion go to a separate buffer).  Preprocessor arguments default
  37. ;; to the last ones entered.  Both the preprocessor name and the
  38. ;; initial flag defaults can be set by the user.  Setting
  39. ;; c-macro-always-prompt to a non-nil value allows one to be always
  40. ;; prompted for the flags, regardless of the prefix used.
  41.  
  42. ;; A c-macro-expansion function is provided for non-interactive use.
  43. ;; A still experimental function c-macro-eval is provided.  It aims at
  44. ;; evaluating the contents of a region by using calc (by Dave
  45. ;; Gillespie).  Select a region and type C-x C-e (if you followed the
  46. ;; suggestions in the INSTALLATION section) or type M-x c-ma RET v
  47. ;; RET.  If you have calc installed, the computed value of the
  48. ;; expression will appear in the message area.  If you give an
  49. ;; interactive C-u prefix the computed value will be shown in signed,
  50. ;; unsigned, hex and boolean representations.  Two C-u's allow to
  51. ;; change the preprocessor flags via prompt.  c-macro-eval works well
  52. ;; for constant expressions, but see the BUG section.
  53.  
  54. ;; A patch to calc 2.02 has been written by Dave Gillespie.  It can
  55. ;; be downloaded via anonymous ftp at fly.cnuce.cnr.it:pub/calc.diff.
  56.  
  57. ;; INSTALLATION ======================================================
  58.  
  59. ;; Put this file on your load-path, byte compile it for increased
  60. ;; speed and put part or all of the following in your ~/.emacs file.
  61.  
  62. ;; To make a directory ~/emacs be in front of your load-path:
  63. ;;(setq load-path (cons (expand-file-name "~/emacs") load-path))
  64. ;;
  65. ;; Suggested keybindings (work only in c-mode):
  66. ;;(define-key c-mode-map "\C-\M-x" 'c-macro-expand)
  67. ;;(define-key c-mode-map "\C-x\C-e" 'c-macro-eval)
  68. ;;
  69. ;; If you want the *Macroexpansion* window to be not higher than
  70. ;; necessary: 
  71. ;;(setq c-macro-shrink-window-p t)
  72. ;;
  73. ;; If you use a preprocessor other than /lib/cpp (be careful to set a
  74. ;; -C option or equivalent in order to make the preprocessor not to
  75. ;; strip the comments):
  76. ;;(setq c-macro-preprocessor "gpp -C")
  77. ;;
  78. ;; If you often use a particular set of flags, and want them to be
  79. ;; the default:
  80. ;;(setq c-macro-default-cppflags "-I /usr/include/local -DDEBUG"
  81. ;;
  82. ;; If you always want the "Preprocessor arguments: " prompt,
  83. ;; regardless of the arguments provided:
  84. ;;(setq c-macro-always-prompt-p t)
  85. ;;
  86. ;; If you want to experiment with the C constant expressions
  87. ;; evaluation feature:
  88. ;;(autoload 'c-macro-eval "cmacexp"
  89. ;;  "C constant expressions evaluation.  Requires calc.  Experimental." t)
  90.  
  91. ;; BUG REPORTS =======================================================
  92.  
  93. ;; Please report bugs, suggestions, complaints and so on to
  94. ;; pot@cnuce.cnr.it (Francesco Potorti`).
  95.  
  96. ;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ==========================
  97.  
  98. ;; - A lot of user visible changes.  See above.
  99. ;; - #line directives are inserted, so __LINE__ and __FILE__ are
  100. ;;   correctly expanded.  Works even with START inside a string, a
  101. ;;   comment or a region #ifdef'd away by cpp. cpp is invoked with -C,
  102. ;;   making comments visible in the expansion.
  103. ;; - All work is done in core memory, no need for temporary files.
  104. ;; - The /lib/cpp process is run synchronously.  This fixes an
  105. ;;   infinite loop bug on Motorola Delta (cpp waiting forever for
  106. ;;   end-of-file, don't know why).  Fixes a similar intermittent
  107. ;;   problem on SunOS 4.1.
  108.  
  109. ;; ACKNOWLEDGEMENTS ==================================================
  110.  
  111. ;; A lot of thanks to Don Maszle who did a great work of testing, bug
  112. ;; reporting and suggestion of new features, to Inge Fricks for her
  113. ;; help with view.el and to Dave Gillespie for his suggestions on
  114. ;; calc's use.  This work has been partially inspired by Don Maszle
  115. ;; and Jonathan Segal's.
  116.  
  117. ;; By the way, I recommend you Inge Frick's view.el.  It works like
  118. ;; the standard view, but *it is not recursive* and has some more
  119. ;; commands.  Moreover it is a minor mode, so you preserve all your
  120. ;; major mode keybindings (well, not always :).  Mail me to obtain a
  121. ;; copy, or get it by anonymous ftp in fly.cnuce.cnr.it:pub/view.el.
  122.  
  123. ;; BUGS ==============================================================
  124.  
  125. ;; calc 2.02 does not handle the C operators "->", ".", "*" (as a
  126. ;; prefix), the composite assignement operators "+=" etc.  It cannot
  127. ;; handle the "," operator and will be confused by ";".  Almost all
  128. ;; these can be defined as no-ops using the Calc's Syntax Tables
  129. ;; feature.  The built-in calc functions will cause problems in
  130. ;; certain circumstances.  c-macro-eval behaves correctly only on
  131. ;; expressions not containing such operators.  Does not distinguish
  132. ;; among integer and real division.
  133.  
  134. ;; If the start point of the region is inside a macro definition the
  135. ;; macro expansion is often inaccurate.
  136.  
  137. ;;; Code:
  138.  
  139. (defvar c-macro-shrink-window-p nil
  140.   "*Non-nil means shrink the *Macroexpansion* window to fit its contents.")
  141.  
  142. (defvar c-macro-always-prompt-p nil
  143.   "*Non-nil means always prompt for preprocessor arguments.")
  144.  
  145. (defvar c-macro-preprocessor "/lib/cpp -C" "\
  146. The preprocessor used by the cmacexp package.
  147.  
  148. If you change this, be sure to preserve the -C (don't strip comments)
  149. option, or to set an equivalent one.")
  150.  
  151. (defvar c-macro-default-cppflags ""
  152.   "Default cpp flags used by c-macro-expand.")
  153.  
  154. (defconst c-macro-buffer-name "*Macroexpansion*")
  155.  
  156. (defun c-macro-expand (start end &optional flag) "\
  157. Expand all C macros occurring in the region using c-macro-preprocessor.
  158. Normally display output in temp buffer.
  159. Prefix arg means replace the region with it.
  160. Prompt for a string of arguments to the preprocessor, (e.g.
  161. -DDEBUG -I ./include) when prefixed with two C-u's.
  162.  
  163. It is intended for interactive use only.
  164. For non interactive use, see the c-macro-expansion function."
  165.  
  166.   (interactive "r\nP")
  167.   (let* ((subst (and flag (not (equal flag '(16)))))
  168.      (inbuf (current-buffer))
  169.      (displaybuf (if subst
  170.              (get-buffer c-macro-buffer-name)
  171.                (get-buffer-create c-macro-buffer-name)))
  172.      (expansion ""))
  173.     ;; Build the command string.
  174.     (if (or c-macro-always-prompt-p (equal flag '(16)))
  175.     (setq c-macro-default-cppflags
  176.           (read-string "Preprocessor arguments: "
  177.                c-macro-default-cppflags)))
  178.     ;; Decide where to display output.
  179.     (if (and subst
  180.          buffer-read-only
  181.          (not (eq inbuf displaybuf)))
  182.     (progn
  183.       (message
  184.        "Buffer is read only: displaying expansion in alternate window")
  185.       (sit-for 2)
  186.       (setq subst nil)
  187.       (or displaybuf
  188.           (setq displaybuf (get-buffer-create c-macro-buffer-name)))))
  189.     ;; Expand the macro and output it.
  190.     (if (interactive-p) (message (c-macro-default-message)))
  191.     (setq expansion
  192.       (c-macro-expansion start end
  193.                  (concat c-macro-preprocessor " "
  194.                      c-macro-default-cppflags)))
  195.     (message (concat (c-macro-default-message) "done"))
  196.     (if subst
  197.     (let ((exchange (= (point) start)))
  198.       (delete-region start end)
  199.       (insert expansion)
  200.       (if exchange
  201.           (exchange-point-and-mark)))
  202.       (set-buffer displaybuf)
  203.       (setq buffer-read-only nil)
  204.       (buffer-flush-undo displaybuf)
  205.       (erase-buffer)
  206.       (insert expansion)
  207.       (set-buffer-modified-p nil)
  208.       (if (string= "" expansion)
  209.       (message "Null expansion")
  210.     (c-macro-display-buffer inbuf))
  211.       (setq buffer-read-only t)
  212.       (bury-buffer displaybuf))))
  213.  
  214.  
  215. ;; Display the current buffer in a window which is either just large
  216. ;; enough to contain the entire buffer, or half the size of the
  217. ;; screen, whichever is smaller.  Put the current buffer in view-mode
  218. ;; if the Inge Frick's view-mode is installed, with buffer to return
  219. ;; to set to RETBUF (if sensible). Do not select the new window.
  220. ;;
  221. ;; Several factors influence window resizing so that the window is
  222. ;; sized optimally if it is created anew, and so that it is messed
  223. ;; with minimally if it has been created by the user.  If the window
  224. ;; chosen for display exists already but contains something else, the
  225. ;; window is not re-sized.  If the window already contains the current
  226. ;; buffer, it is never shrunk, but possibly expanded.  Finally, if the
  227. ;; variable c-macro-shrink-window-p is nil the window size is *never*
  228. ;; changed.
  229. (defun c-macro-display-buffer (retbuf)
  230.  
  231.   (goto-char (point-min))
  232.   (c-mode)
  233.   (require 'view)            ;load view.el
  234.   (let ((oldwinheight (window-height))
  235.     (alreadythere            ;the window was already there
  236.      (get-buffer-window (current-buffer)))
  237.     (popped nil)            ;the window popped changing the layout 
  238.     (niceview            ;is this Inge Fricks's view.el?
  239.      (boundp 'view-kill-when-finished)))
  240.  
  241.     (or alreadythere
  242.     (progn
  243.       (display-buffer (current-buffer) t)
  244.       (setq popped (/= oldwinheight (window-height)))))
  245.     (if niceview
  246.     (view-mode 1))            ;set view mode
  247.     (if (and c-macro-shrink-window-p    ;user wants fancy shrinking :\)
  248.          (or alreadythere popped))
  249.     ;; Enlarge up to half screen, or shrink properly.
  250.     (let ((oldwin (selected-window))
  251.           (minheight 0)
  252.           (maxheight 0))
  253.       (save-excursion
  254.         (select-window (get-buffer-window (current-buffer)))
  255.         (setq minheight (if alreadythere
  256.                 (window-height)
  257.                   window-min-height))
  258.         (setq maxheight (/ (screen-height) 2))
  259.         (enlarge-window (- (min maxheight
  260.                     (max minheight
  261.                      (+ 2 (vertical-motion 1000000))))
  262.                    (window-height)))
  263.         (goto-char (point-min))
  264.         (select-window oldwin))))))
  265.  
  266.  
  267. (defun c-macro-expansion (start end cppcommand) "\
  268. Expands the region between START and END in the current buffer using
  269. the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\").  Be sure
  270. to use a -C (don't strip comments) or equivalent option.
  271. Returns the output as a string."
  272.  
  273. ;; Copy the current buffer's contents to a temporary hidden buffer.
  274. ;; Delete from END to end of buffer.  Insert a preprocessor #line
  275. ;; directive at START and after each #endif following START that are
  276. ;; not inside a comment or a string.  Put all the strings thus
  277. ;; inserted (without the "line" substring) in a list named linelist.
  278. ;; If START is inside a comment, prepend "*/" and append "/*" to the
  279. ;; #line directive.  If inside a string, prepend and append "\"".
  280. ;; Preprocess the buffer contents, then look for all the lines stored
  281. ;; in linelist starting from end of buffer.  The last line so found is
  282. ;; where START was, so return the substring from point to end of
  283. ;; buffer. 
  284.   (let ((inbuf (current-buffer))
  285.     (outbuf (get-buffer-create " *C Macro Expansion*"))
  286.     (filename (if (and buffer-file-name
  287.                (string-match (regexp-quote default-directory)
  288.                      buffer-file-name))
  289.               (substring buffer-file-name (match-end 0))
  290.             (buffer-name)))
  291.     (linenum 0)
  292.     (linelist ()))
  293.     (unwind-protect
  294.     (save-excursion
  295.       (save-restriction
  296.         (widen)
  297.         (set-buffer outbuf)
  298.         (setq buffer-read-only nil)
  299.         (erase-buffer)
  300.         (set-syntax-table c-mode-syntax-table)
  301.         (insert-buffer-substring inbuf 1 end))
  302.  
  303.       ;; We have copied inbuf to outbuf.  Point is at end of
  304.       ;; outbuf.  Insert a space at the end, so cpp can correctly
  305.       ;; parse a token ending at END. 
  306.  
  307.       (insert " ")
  308.  
  309.       ;; Now we insert the #line directives after all #endif or
  310.       ;; #else following START. 
  311.       ;(switch-to-buffer outbuf) (debug)    ;debugging instructions
  312.       (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move)
  313.         (if (equal (nthcdr 3 (parse-partial-sexp 1 (point)))
  314.                '(nil nil nil 0)) ;neither in string nor in
  315.                      ;comment nor after quote
  316.         (progn
  317.           (goto-char (match-end 0))
  318.           (setq linenum (count-lines 1 (point)))
  319.           (setq linelist
  320.             (cons (format "\n# %d \"%s\"\n" linenum filename)
  321.                   linelist))
  322.           (insert (car linelist))
  323.           (skip-chars-backward "^#")
  324.           (insert "line")
  325.           (goto-char (match-beginning 0)))))
  326.  
  327.       ;; We are at START.  Insert the first #line directive.  This
  328.       ;; must work even inside a string or comment, or after a
  329.       ;; quote.
  330.       (setq linenum (+ (count-lines 1 (point))
  331.                (if (bolp) 1 0)))
  332.       (setq linelist
  333.         (cons
  334.          (let* ((startstat (parse-partial-sexp 1 start))
  335.             (startinstring (nth 3 startstat))
  336.             (startincomment (nth 4 startstat))
  337.             (startafterquote (nth 5 startstat)))
  338.            (concat (if startafterquote " ")
  339.                (cond (startinstring "\"") (startincomment "*/"))
  340.                (format "\n# %d \"%s\"\n" linenum filename)
  341.                (cond (startinstring "\"") (startincomment "/*"))
  342.                (if startafterquote "\\")))
  343.          linelist))
  344.       (insert (car linelist))
  345.       (skip-chars-backward "^#")
  346.       (insert "line")
  347.  
  348.       ;; Call the preprocessor.
  349.       (call-process-region 1 (point-max) "sh" t t nil "-c"
  350.                    (concat cppcommand " 2>/dev/null"))
  351.  
  352.       ;; Look for the `# nn "file.c"' lines from the last to the first
  353.       ;; and delete them.
  354.       (setq linelist (reverse linelist))
  355.       (while (progn
  356.            (if (search-backward (car linelist) nil t)
  357.                (replace-match ""))
  358.            (setq linelist (cdr linelist))))
  359.       
  360.       ;; Compute the return value, keeping in account the space
  361.       ;; inserted at the end of the buffer.
  362.       (buffer-substring (point) (max (point) (- (point-max) 1))))
  363.  
  364.       ;; Cleanup.
  365.       (kill-buffer outbuf))))
  366.  
  367.  
  368. ;; Experimental.  With an argument, print signed, unsigned, hex and
  369. ;; boolean representations.
  370. (defun c-macro-eval (start end &optional flag) "\
  371. Expand region using cpp and evaluate it using calc.
  372. Interactively print value in minibuffer and push it on the kill ring.
  373. With a C-u argument shows the evaluation in a variety of formats.
  374. With two C-u's prompts the user for a string of flags to the preprocessor.
  375.  
  376. Non interactively returns value of region between START and END
  377. as a string.  Several formats are used if optional FLAG is non-nil."
  378.  
  379.   (interactive "r\nP")
  380.   (or (fboundp 'calc-eval)
  381.       (require 'calc))
  382.   (if (or c-macro-always-prompt-p (equal flag '(16)))
  383.       (setq c-macro-default-cppflags
  384.         (read-string "Preprocessor arguments: "
  385.              c-macro-default-cppflags)))
  386.  
  387.   ;; Expand the region.
  388.   (if (interactive-p) (message (c-macro-default-message)))
  389.   (let ((evaluation
  390.      (c-macro-expansion start end
  391.                 (concat c-macro-preprocessor " "
  392.                     c-macro-default-cppflags)))
  393.     (evalbuf (get-buffer-create " *Macro Evaluation*")))
  394.     (unwind-protect
  395.     (save-excursion
  396.       (set-buffer evalbuf)
  397.       (setq buffer-read-only nil)
  398.       (erase-buffer)
  399.       (insert evaluation)
  400.  
  401.       ;; Evaluate expression(s).
  402.       (if (interactive-p)
  403.           (message "Invoking calc..."))
  404.       (setq evaluation
  405.         (let ((calc-eval-error t))
  406.           (calc-eval (list (buffer-string) 'calc-language 'c))))
  407.       (erase-buffer)
  408.       (cond
  409.        (flag
  410.         (insert (calc-eval (list evaluation
  411.                      'calc-language 'c
  412.                      'calc-simplify-mode 'binary))
  413.             "(u)" " == "
  414.             (calc-eval (list evaluation
  415.                      'calc-language 'c
  416.                      'calc-word-size (- calc-word-size)
  417.                      'calc-simplify-mode 'binary))
  418.             "(d)" " == "
  419.             (calc-eval (list evaluation
  420.                      'calc-language 'c
  421.                      'calc-number-radix 16
  422.                      'calc-simplify-mode 'binary))
  423.             "(x)")
  424.         (save-excursion
  425.           (insert " == " (calc-eval (list evaluation
  426.                           'calc-language 'c
  427.                           'calc-number-radix 16
  428.                           'calc-simplify-mode 'binary))))
  429.         (while (re-search-forward "0x\\([^,]+\\)\\(, \\|\\'\\)" nil t)
  430.           (if (string= "0"
  431.                (buffer-substring (match-beginning 1)
  432.                          (match-end 1)))
  433.           (replace-match "FALSE\\2")
  434.         (replace-match "TRUE\\2"))))
  435.        (t
  436.         (insert evaluation)))
  437.  
  438.       ;; Output the evaluation.
  439.       (if (interactive-p)
  440.           (progn
  441.         (copy-region-as-kill 1 (point-max))
  442.         (message (buffer-string)))
  443.         (buffer-string)))
  444.       (kill-buffer evalbuf))))
  445.  
  446. (defun c-macro-default-message ()
  447.   (format "Invoking %s%s%s on region..."
  448.       c-macro-preprocessor
  449.       (if (string= "" c-macro-default-cppflags) "" " ")
  450.       c-macro-default-cppflags))
  451.  
  452. (provide 'cmacexp)
  453.  
  454. ;;; cmacexp.el ends here.
  455.